# !/usr/bin/python
# -*- coding: UTF-8 -*-
"""
----------------------------------------------------------------------------------------------
@File             : protocolGateLog.py
@Creat Time       : 2020-07-08 09:53:15
@Author           : Jack
@Site             :
@Software         : PyCharm
@file Conent      : 日志任务函数文件
----------------------------------------------------------------------------------------------
@Revise history   :
@Version          : V12
@Amendant records :
     1)Jack  2020-07-08  增加了文档头部新注释内容;
     2)Jack  2020-07-14  增加了日志级数，增加了单独显示某一任务的代码，传入任务名称不再需要添加冒号;
     3)Jack  2020-07-15  日志文件大小到达阈值不再覆盖重写，转为写入新的文件，
                         并对日志名称做了修改;
     4)Jack  2020-07-16  日志内容增加了任务名称的占位符及其宽度;
     5)Jack  2020-07-17  修改了任务名称占位符的宽度，增加了日志文件的编码格式，utf-8,增加了其他日志内
                         容的占位符及其宽度，对写入日志文件的内容进行了格式规范,对打印内容增加了互斥信
                         号量，避免打印错位的问题；
     6)Jack  2020-07-18  增加了命令的注释信息;
     7)Jack  2020-07-26  增加了打印及写入文件日志级别的判断，将符合级别的日志打印及写入；
     8)Jack  2020-07-28  logTaskFunction函数传入参数fileName为文件路径，在存储前添加指定的文件名；
     9)Jack  2020-07-28  sendLogMessage函数在打印及写入日志条件判断方面，在原有的日志级别基础上，又增
                         加了对任务名称是否为勾选内容的判断；
     10)Jack  2020-07-28 sendLogMessage函数因IoT任务名称特殊，与Data文件定义的任务常量不同，增加了单
                         独判断机制；
     11)Jack  2020-07-30 在sendLogMessage函数增加了gWriteLogTaskListMutex，用于访问写入日志选项列
                         表的互斥信号量的使用方法；
     12)Jack 2020-08-03  在sendLogMessage函数中增加了是否在屏幕打印日志内容的操作；
     13)Jack 2020-08-03  在sendLogMessage函数中进行了修改，删除了日志级别的打印；
     14)Jack 2020-08-05  进行了日志内容的规范；
     15)Jack 2020-08-06  将日志在屏幕显示操作与写入文件操作进行分离处理；
----------------------------------------------------------------------------------------------
"""

import EdgeGateData
import time                 # 导入    time           模块，用于获取当前时间信息
import os                   # 导入    os             模块，用于创建文件夹等操作


"""
---------------------------------------------------------------------------
@函数名称：sendLogMessage()
@函数功能：
    1)发送日志内容到全局变量gLogMessageQueue
    2)

@参数：
    1）taskName,调用的任务名称；
    2）content,日志内容；
    3）para,参数内容，用于出错后返回参数；
    4）level,级数，日志级别

@其他说明：
    1）除了任务名称，日志内容和相关参数，还增加了日志对应时间
---------------------------------------------------------------------------
@Revise history   :
@Amendant records :
     1)Jack  2020-07-08  增加了函数头部新注释内容；
     2)Jack  2020-07-14  增加了日志级别参数，以及用于显示某一任务日志的代码,
                         传入任务名称在本函数中添加冒号，无需手动添加；
     3)Jack  2020-07-16  日志内容增加了任务名称的占位符及其宽度；
     4)Jack  2020-07-17  根据任务名称长度，将占位符宽度改为20,增加了其他日志内容
                         的占位符及其宽度，对写入日志文件的内容进行了格式规范，对
                         打印内容增加了互斥信号量，避免打印错位的问题
     5)Jack  2020-07-18  增加了命令的注释信息；
     6)Jack  2020-07-26  增加了打印及写入文件日志级别的判断，将符合级别的日志打印及
                         写入；
     7)Jack  2020-07-28  在打印及写入日志条件判断方面，在原有的日志级别基础上，又增
                         加了对任务名称是否为勾选内容的判断；
     8)Jack  2020-07-28  因IoT任务名称特殊，与Data文件定义的任务常量不同，增加了单
                         独判断机制；
     9)Jack  2020-07-30  增加了gWriteLogTaskListMutex，用于访问写入日志选项列表的
                         互斥信号量的使用方法；
     10)Jack 2020-08-03  增加了是否在屏幕打印日志内容的操作；
     11)Jack 2020-08-03  删除了日志级别的打印；
     12)Jack 2020-08-05  进行了日志内容的规范；
     13)Jack 2020-08-06  将日志在屏幕显示操作与写入文件操作进行分离处理；
---------------------------------------------------------------------------
"""
def sendLogMessage(taskName,content,para,level=EdgeGateData.const.LOG_FILTER_L2_FLAG):
    funTaskName = EdgeGateData.const.TASK_LOG_NAME+": "
    writeFlag   = EdgeGateData.gWriteFlag                           # 用于判断是否写日志,默认值与全局变量gWriteFlag保持一致
    printFlag   = False                                                 # 用于判断是否打印日志，默认不打印
    newTaskName = taskName+": "
    localTime   = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())  # 获取本地时间
    logPrintMsg = '%s %s %s %s' % (localTime,
                                   newTaskName,
                                   content,
                                   para)                                # 生成用于屏幕显示的日志内容
    logWriteMsg = '%s %20s %-50s %2s the level of log:%s' %(localTime,
                                                            newTaskName,
                                                            content,
                                                            para,
                                                            level)      # 生成用于写入文件的日志内容
    if EdgeGateData.const.TASK_IOT_NAME in taskName:                # IoT任务的日志
        taskName    =   EdgeGateData.const.TASK_IOT_NAME            # 暂时修改taskName,改为Data文件中的IoT任务常量
    '''
    根据用户设置，判断是否打印日志
    '''
    EdgeGateData.gWriteLogTaskListMutex.acquire()                   # 日志勾选项列表互斥锁的请求
    try:
        if taskName in EdgeGateData.gWriteLogTaskList \
                and level <= EdgeGateData.gLogWriteLevel \
                and EdgeGateData.gPrintFlag == True:                # 日志任务名在勾选项内，且级别符合要求,且用户要求输出到屏幕
            printFlag   =   True                                        # 将打印标识符置为True
        else:                                                           # 日志不符合打印要求
            printFlag   =   False                                       # 将打印标识符置为False
    finally:
        EdgeGateData.gWriteLogTaskListMutex.release()               # 日志勾选项列表互斥锁的释放
    '''
    根据用户设置，判断是否写入日志
    '''
    if EdgeGateData.gWriteFlag  == True:                            # 用户选择全部写入
        writeFlag   =   True                                            # 将写入标识符置为True，表示可写入
    else:                                                               # 用户选择根据设定结果写入
        EdgeGateData.gWriteLogTaskListMutex.acquire()               # 日志勾选项列表互斥锁的请求
        try:
            if taskName in EdgeGateData.gWriteLogTaskList \
                    and level <= EdgeGateData.gLogWriteLevel:       # 日志符合写入要求
                writeFlag   = True                                      # 将写入标识符置为True，表示可写入
            else:                                                       # 日志不符合写入要求
                writeFlag   = False                                     # 将写入标识符置为False，表示补课写入
        finally:
            EdgeGateData.gWriteLogTaskListMutex.release()           # 日志勾选项列表互斥锁的释放
    '''
    日志输出到屏幕的处理
    '''
    if printFlag == True:                                               # 符合打印要求
        EdgeGateData.gPrintMutex.acquire()                          # 打印互斥锁请求
        print(logPrintMsg)                                              # 在屏幕输出日志
        EdgeGateData.gPrintMutex.release()                          # 打印互斥锁释放
    else:                                                               # 不符合打印要求
        pass
    '''
    写入文件的处理
    '''
    if writeFlag == True:                                               # 符合写入要求
        EdgeGateData.gLogMessageQueueMutex.acquire()                # 日志队列互斥锁请求
        try:
            EdgeGateData.gLogMessageQueue.put(logWriteMsg)          # 将写入文件的日志存放于日志队列中
        except Exception as e:
            print(funTaskName,
                          'the function of sendLogMessage can not  store data in the log queue:',
                          e)                                            # 打印无法写入日志队列信息
        finally:
            EdgeGateData.gLogMessageQueueMutex.release()            # 日志队列互斥锁释放
    else:                                                               # 不符合写入要求
        pass

"""
---------------------------------------------------------------------------
@函数名称：logTaskFunction()
@函数功能：
    1)日志任务处理函数，处理全局变量gLogMessageQueue的内容，将数据写入日志文件
    2)

@参数：
    1）fileName,带路径文件名；
    2）logFileSize,日志文件大小；

@其他说明：
    1）日志记录格式：时间（函数中获得） 任务名称 日志内容 参数 日志级别；
    2）日志文件大小达到上限时，重新创建一个文件进行日志存储；
---------------------------------------------------------------------------
@Revise history   :
@Amendant records :
     1)Jack  2020-07-08  增加了函数头部新注释内容;
     2)Jack  2020-07-15  日志文件大小到达阈值不再覆盖重写，转为写入新的文件，
                         并对日志名称做了修改;
     3)Jack  2020-07-17  增加了日志文件的编码格式，utf-8;
     4)Jack  2020-07-18  增加了命令的注释信息;
     5)Jack  2020-07-28  传入参数fileName为文件路径，在存储前添加指定的文件名；
     6)Jack  2020-08-05  进行了日志内容的规范；
---------------------------------------------------------------------------
"""
def logTaskFunction(fileName,logFileSize):
    taskName = EdgeGateData.const.TASK_LOG_NAME                                         # 获取任务名称
    sendLogMessage(taskName,
                   'Log task is started.',
                   EdgeGateData.const.INIT_VALUE,
                   EdgeGateData.const.LOG_FILTER_L0_FLAG)                               # 任务启动，发送启动日志
    fileName        = fileName + '/' + EdgeGateData.gLogFileName                        # 将日志文件名与路径进行拼接
    oldFileName     = fileName                                                              # 保存初始日志文件名，不含时间
    fileName        = oldFileName % (time.strftime(" %Y-%m-%d %H-%M-%S", time.localtime())) # 为日志文件名加时间
    while True:                                                                             # 设置死循环
        while EdgeGateData.gLogMessageQueue.qsize() > 0:                                # 日志队列不为空
            EdgeGateData.gLogMessageQueueMutex.acquire()                                # 日志队列互斥锁请求
            data = EdgeGateData.gLogMessageQueue.get()                                  # 从队列获取日志消息
            EdgeGateData.gLogMessageQueueMutex.release()                                # 日志队列互斥锁释放
            if os.path.exists(fileName):                                                    # 该文件名已存在
                if os.path.getsize(fileName)/EdgeGateData.const.FILEUNIT >= logFileSize:# 该文件大小大于设定的上限
                    fileName = oldFileName % (time.strftime(" %Y-%m-%d %H-%M-%S", time.localtime()))    # 更新文件名称
            '''
            获取路径对应文件夹信息
            '''
            if '\\' in fileName:                                            # 以\作为层级区分
                index = 0                                                   # 设置索引，用于寻找文件所在目录层
                for i, score in enumerate(fileName):                        # 依次遍历路径
                    if score == '\\':                                       # 当前字符为\
                        index = i                                           # 索引更新
                path = fileName[:index + 1]                                 # 获取文件所在文件夹路径
                try:
                    if not os.path.exists(path):                            # 该文件夹不存在
                        os.makedirs(path)                                   # 创建文件夹
                except Exception as e:
                    sendLogMessage(taskName,
                                   'unable to create log folder:',
                                   e,
                                   EdgeGateData.const.LOG_FILTER_L1_FLAG)   # 创建失败，发送失败日志
            elif '/' in fileName:                                           # 以/作为层级区分
                index = 0                                                   # 设置索引，用于寻找文件所在目录层
                for i,score in enumerate(fileName):                         # 依次遍历路径
                    if score == '/':                                        # 当前字符为/
                        index = i                                           # 索引更新
                path = fileName[:index+1]                                   # 获取文件所在文件夹路径
                try:
                    if not os.path.exists(path):                            # 该文件夹不存在
                        os.makedirs(path)                                   # 创建文件夹
                except Exception as e:
                    sendLogMessage(taskName,
                                   'unable to create log folder:',
                                   e,
                                   EdgeGateData.const.LOG_FILTER_L1_FLAG)   # 创建失败，发送失败日志
            try:
                with open(fileName,'a',encoding='utf-8') as f:              # 以utf-8编码格式，续写的方式打开文件
                    f.write('%s\n\n' %(data))                               # 将日志消息写入文件，为了区分，增加空行
            except Exception as e:
                sendLogMessage(taskName,
                               'unable to write log:',
                               e,
                               EdgeGateData.const.LOG_FILTER_L1_FLAG)   # 写文件失败，发送失败日志
